#include "AsyncRunner.h"

AsyncRunner::AsyncRunner(const QString patientName, const QString patientId,
                         const QVector<QString>& fileList, const QString runningOptions)
    :patientName(patientName), patientId(patientId), fileList(fileList), runningOptions(runningOptions)
{
    qDebug() << "name" << patientName << Qt::endl
             << "id " << patientId << Qt::endl
             << "list" << fileList[0] << Qt::endl
             << "options" << runningOptions << Qt::endl;

}

void AsyncRunner::run(){
    try{
        loopMain();
    }catch(const std::exception &e){
        qCritical() << "An error in AsyncRunner " << e.what();

        AsyncRunnerResult res;
        res.taskStatus = ResultEnum::FAIL;
        res.taskId = taskId;
        res.options = runningOptions;
        res.errMsg = e.what();

        emit AsyncRunner::AsyncDone(res);
    }
}

void AsyncRunner::loopMain(){
    double tS, tE;

    // create task
    tS = (double) clock();
    SyncTaskApi::RemoteApiResult taskInfo = createTask();
    tE = (double) clock();

    if (taskInfo.code == -1) throw std::runtime_error(taskInfo.errMsg.toStdString());
    else taskId = taskInfo.result;
    qDebug() << "Create done, cost " << (tE - tS) / CLOCKS_PER_SEC << "s";

    // simple upload
    tS = (double) clock();
    SyncTaskApi::RemoteApiResult uploadInfo = uploadTask();
    tE = (double) clock();

    if (uploadInfo.code == -1) throw std::runtime_error(uploadInfo.errMsg.toStdString());
    qDebug() << "Upload done, cost " << (tE - tS) / CLOCKS_PER_SEC << "s";

    // start task
    tS = (double) clock();
    SyncTaskApi::RemoteApiResult putTaskInfo = putTask();
    tE = (double) clock();

    if (putTaskInfo.code == -1) throw std::runtime_error(putTaskInfo.errMsg.toStdString());
    qDebug() << "Put task done, cost " << (tE - tS) / CLOCKS_PER_SEC << "s";

    // query progress
    while (true){
        tS = (double) clock();
        SyncTaskApi::ApiTaskProgress queryInfo = queryTask();
        tE = (double) clock();
        qDebug() << "query done, cost " << (tE - tS) / CLOCKS_PER_SEC << "s";

        if (queryInfo.code == -1) throw std::runtime_error(queryInfo.errMsg.toStdString());
        if (queryInfo.taskStatus == 500) throw std::runtime_error(queryInfo.taskMsg.toStdString());

        // progress could emit to anothor handler
        if (queryInfo.taskStatus == 200) break;

        QThread::sleep(3);
    }

    // get json
    tS = (double) clock();
    SyncTaskApi::RemoteApiResult resultInfo = queryResult();
    tE = (double) clock();
    qDebug() << "Result done, cost " << (tE - tS) / CLOCKS_PER_SEC << "s";

    // save json to file
    saveResultToFile(Helper::unicode16to8(resultInfo.result));

    // emit
    AsyncRunnerResult res;
    res.taskStatus = ResultEnum::SUCCESS;
    res.taskId = taskId;
    res.options = runningOptions;
    res.okRes = resultInfo.result;
    emit AsyncRunner::AsyncDone(res);
}

bool AsyncRunner::saveResultToFile(const QString res){
    QString s = QDir(cachePath).filePath(patientName);
    s = QDir(s).filePath(patientId);
    if (!QDir(s).exists()){
       QDir().mkpath(s);
    }

    QString fileName = Helper::_fixName(QFileInfo(fileList[0]).fileName());
    size_t lastIndex = fileName.lastIndexOf('.');
    QString leftStr = fileName.left(lastIndex);
    const QString newFileName = leftStr + ".json";
    const QString saveJsonPath = QDir(s).filePath(newFileName);

    QFile f(saveJsonPath);
    if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)){
        return false;
    }

    QTextStream stream(&f);
    stream << res;

    f.close();
    return true;
}

SyncTaskApi::RemoteApiResult AsyncRunner::createTask(){
    try{
        return SyncTaskApi::createRemoteTask(TaskApi::DEFAULT_USER_NAME);
    }catch(const std::exception &e){
        qCritical() << "An error in createTask " << e.what();
        return SyncTaskApi::RemoteApiResult{-1, "", e.what()};
    }
}

SyncTaskApi::RemoteApiResult AsyncRunner::uploadTask(){
    try{
        return SyncTaskApi::uploadFileToTask(taskId, fileList);
    }catch(const std::exception &e){
        qCritical() << "An error in uploadTask " << e.what();
        return SyncTaskApi::RemoteApiResult{-1, "", e.what()};
    }
}

SyncTaskApi::RemoteApiResult AsyncRunner::putTask(){
    try{
        return SyncTaskApi::putTask(taskId, runningOptions);
    }catch(const std::exception &e){
        qCritical() << "An error in putTask " << e.what();
        return SyncTaskApi::RemoteApiResult{-1, "", e.what()};
    }
}

SyncTaskApi::ApiTaskProgress AsyncRunner::queryTask(){
    try{
        return SyncTaskApi::taskQuery(taskId);
    }catch(const std::exception &e){
        qCritical() << "An error in queryTask " << e.what();
        return SyncTaskApi::ApiTaskProgress{-1, "", e.what(), -1, "", 0.0};
    }
}

SyncTaskApi::RemoteApiResult AsyncRunner::queryResult(){
    try{
        QString fileName = QFileInfo(fileList[0]).fileName();
        return SyncTaskApi::resultQuery(taskId, fileName, runningOptions);
    }catch(const std::exception &e){
        qCritical() << "An error in putTask " << e.what();
        return SyncTaskApi::RemoteApiResult{-1, "", e.what()};
    }
}
